//! RK3588 PMU (Power Management Unit) register definitions
//!
//! Based on RK3588 TRM Chapter 7 - Power Management Unit
//! PMU registers are divided into three parts:
//! - PMU0: 0x0000~0x3FFF (ALIVE in VD_PMU)
//! - PMU1: 0x4000~0x7FFF (PD_PMU1)
//! - PMU2: 0x8000~0xBFFF (ALIVE in VD_LOGIC)

/// PMU register offsets
pub mod offset {
    // PMU0 registers (0x0000~0x3FFF)
    pub const PMU_PWR_CON0: usize = 0x0000;
    pub const PMU_WAKEUP_INT_CON_P0: usize = 0x0008;
    pub const PMU_WAKEUP_INT_STS_P0: usize = 0x000C;
    pub const PMU_PMIC_STABLE_CNT_P0: usize = 0x0010;
    pub const PMU_WAKEUP_RST_CLR_CNT_P0: usize = 0x0014;
    pub const PMU_OSC_STABLE_CNT_P0: usize = 0x0018;
    pub const PMU_PMU1_PWR_CHAIN_STABLE_CON: usize = 0x001C;
    pub const PMU_DDR_RET_CON0_P0: usize = 0x0020;
    pub const PMU_DDR_RET_CON1_P0: usize = 0x0024;
    pub const PMU_INFO_TX_CON: usize = 0x0030;

    // PMU1 registers (0x4000~0x7FFF)
    pub const PMU_VERSION: usize = 0x4000;
    pub const PMU_PWR_CON1: usize = 0x4004;
    pub const PMU_GLB_POWER_STS: usize = 0x4008;
    pub const PMU_INT_MASK_CON: usize = 0x400C;
    pub const PMU_WAKEUP_INT_CON: usize = 0x4010;
    pub const PMU_WAKEUP_INT_STS: usize = 0x4014;

    // DDR channel power control
    pub const PMU_DDR_CH0_PWR_CON: usize = 0x4020;
    pub const PMU_DDR_CH1_PWR_CON: usize = 0x4024;
    pub const PMU_DDR_CH2_PWR_CON: usize = 0x4028;
    pub const PMU_DDR_CH3_PWR_CON: usize = 0x402C;

    // DDR channel software power control
    pub const PMU_DDR_CH0_PWR_SFTCON: usize = 0x4030;
    pub const PMU_DDR_CH1_PWR_SFTCON: usize = 0x4034;
    pub const PMU_DDR_CH2_PWR_SFTCON: usize = 0x4038;
    pub const PMU_DDR_CH3_PWR_SFTCON: usize = 0x403C;

    pub const PMU_DDR_POWER_STS: usize = 0x4040;
    pub const PMU_DDR_STS: usize = 0x4044;

    // CRU power control
    pub const PMU_CRU_PWR_CON: usize = 0x4050;
    pub const PMU_CRU_PWR_SFTCON: usize = 0x4054;
    pub const PMU_CRU_POWER_STS: usize = 0x4058;

    // PLL power control
    pub const PMU_PLLPD_CON0: usize = 0x4060;
    pub const PMU_PLLPD_CON1: usize = 0x4064;
    pub const PMU_PLLPD_SFTCON0: usize = 0x4068;
    pub const PMU_PLLPD_SFTCON1: usize = 0x406C;
    pub const PMU_PLL_POWER_STS0: usize = 0x4070;
    pub const PMU_PLL_POWER_STS1: usize = 0x4074;

    // VD voltage domain power control
    pub const PMU_VDPD_CON0: usize = 0x4080;
    pub const PMU_VDPD_CON1: usize = 0x4084;
    pub const PMU_VDPD_SFTCON0: usize = 0x4088;
    pub const PMU_VDPD_SFTCON1: usize = 0x408C;
    pub const PMU_VD_POWER_STS0: usize = 0x4090;
    pub const PMU_VD_POWER_STS1: usize = 0x4094;

    // Power gating software control
    pub const PMU_PWR_GATE_SFTCON0: usize = 0x40A0;
    pub const PMU_PWR_GATE_SFTCON1: usize = 0x40A4;
    pub const PMU_PWR_GATE_SFTCON2: usize = 0x40A8;

    // Power gating status
    pub const PMU_PWR_GATE_STS0: usize = 0x40B0;
    pub const PMU_PWR_GATE_STS1: usize = 0x40B4;

    // Memory power gating control
    pub const PMU_MEM_PWR_GATE_SFTCON0: usize = 0x40C0;
    pub const PMU_MEM_PWR_GATE_SFTCON1: usize = 0x40C4;
    pub const PMU_MEM_PWR_GATE_SFTCON2: usize = 0x40C8;

    // Memory power gating status
    pub const PMU_MEM_PWR_GATE_STS0: usize = 0x40D0;
    pub const PMU_MEM_PWR_GATE_STS1: usize = 0x40D4;

    // BIU idle software control
    pub const PMU_IDLE_SFTCON0: usize = 0x4100;
    pub const PMU_IDLE_SFTCON1: usize = 0x4104;
    pub const PMU_IDLE_SFTCON2: usize = 0x4108;

    // BIU idle status
    pub const PMU_IDLE_STS0: usize = 0x4110;
    pub const PMU_IDLE_STS1: usize = 0x4114;

    // BIU idle acknowledgment status
    pub const PMU_IDLE_ACK_STS0: usize = 0x4118;
    pub const PMU_IDLE_ACK_STS1: usize = 0x411C;

    // BISR (Built-In Self-Repair) control registers
    pub const PMU_BISR_CON0: usize = 0x4140;
    pub const PMU_BISR_CON1: usize = 0x4144;
    pub const PMU_BISR_CON2: usize = 0x4148;
    pub const PMU_BISR_CON3: usize = 0x414C;
    pub const PMU_BISR_CON4: usize = 0x4150;
    pub const PMU_BISR_CON5: usize = 0x4154;
    pub const PMU_BISR_CON6: usize = 0x4158;
    pub const PMU_BISR_CON10: usize = 0x4168;
    pub const PMU_BISR_CON11: usize = 0x416C;
    pub const PMU_BISR_CON12: usize = 0x4170;
    pub const PMU_BISR_CON13: usize = 0x4174;

    // BISR status registers
    pub const PMU_BISR_STS0: usize = 0x4180;
    pub const PMU_BISR_STS1: usize = 0x4184;
    pub const PMU_BISR_STS4: usize = 0x4190;
    pub const PMU_BISR_STS5: usize = 0x4194;
}

/// CRU (Clock and Reset Unit) register offsets
/// Used for system reboot functionality
pub mod cru_offset {
    /// Global reset counter threshold register
    pub const CRU_GLB_CNT_TH: usize = 0x0C00;

    /// First-level global software reset value register
    pub const CRU_GLB_SRST_FST_VALUE: usize = 0x0C08;

    /// Second-level global software reset value register
    pub const CRU_GLB_SRST_SND_VALUE: usize = 0x0C0C;
}

/// CRU magic values
pub mod cru_magic {
    /// First-level global software reset magic value (complete reset)
    pub const GLB_SRST_FST_MAGIC: u32 = 0xfdb9;

    /// Second-level global software reset magic value (state-preserving reset)
    pub const GLB_SRST_SND_MAGIC: u32 = 0xeca8;
}

/// System reboot level
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RebootLevel {
    /// First-level reset: Complete reset, resets almost all logic, preserving
    /// only registers that support hardware reset only
    First,
    /// Second-level reset: State-preserving reset, resets almost all logic but
    /// preserves GRFs and GPIOs state
    Second,
}

/// Power domain ID (corresponds to PMU_PWR_GATE_SFTCON register bits)
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PowerDomain {
    // PMU_PWR_GATE_SFTCON0 (0x40A0)
    GPU         = 0,
    NPU         = 1,
    VCODEC      = 2,
    NPUTOP      = 3,
    NPU1        = 4,
    NPU2        = 5,
    VENC0       = 6,
    VENC1       = 7,
    RKVDEC0     = 8,
    RKVDEC1     = 9,
    VDPU        = 10,
    RGA30       = 11,
    AV1         = 12,
    VI          = 13,
    FEC         = 14,
    ISP1        = 15,

    // PMU_PWR_GATE_SFTCON1 (0x40A4)
    RGA31       = 16,
    VOP         = 17,
    VO0         = 18,
    VO1         = 19,
    AUDIO       = 20,
    PHP         = 21,
    GMAC        = 22,
    PCIE        = 23,
    NVM         = 24,
    NVM0        = 25,
    SDIO        = 26,
    USB         = 27,
    SECURE      = 28,
    SDMMC       = 29,
    CRYPTO      = 30,
    CENTER      = 31,

    // PMU_PWR_GATE_SFTCON2 (0x40A8)
    VOPCLUSTER0 = 32,
    VOPCLUSTER1 = 33,
    VOPCLUSTER2 = 34,
    VOPCLUSTER3 = 35,
    VOPDSC8K    = 36,
    VOPDSC4K    = 37,
    VOPESMART   = 38,
}

impl PowerDomain {
    /// Get register offset and bit mask for power domain
    ///
    /// Returns: (offset, mask, write_mask)
    /// For some special domains (such as GMAC), offset may differ from standard
    /// registers
    pub fn register_info(&self) -> (usize, u32, u32) {
        // GMAC requires special handling - uses offset 0x4 (relative to SFTCON1)
        if matches!(self, PowerDomain::GMAC) {
            let mask = 1u32 << 6; // BIT(6)
            let write_mask = mask << 16;
            return (offset::PMU_PWR_GATE_SFTCON1 + 0x4, mask, write_mask);
        }

        let id = *self as u8;
        let reg_offset = match id {
            0..=15 => offset::PMU_PWR_GATE_SFTCON0,
            16..=31 => offset::PMU_PWR_GATE_SFTCON1,
            32..=38 => offset::PMU_PWR_GATE_SFTCON2,
            _ => unreachable!(),
        };

        let bit = id % 16;
        let mask = 1u32 << bit;
        let write_mask = mask << 16; // RK3588 uses write mask mechanism

        (reg_offset, mask, write_mask)
    }

    /// Get power domain status register offset and bit mask
    ///
    /// Returns: Some((offset, mask)) if status register exists
    ///          None if the domain has no status register (such as GMAC)
    pub fn status_register_info(&self) -> Option<(usize, u32)> {
        // Some domains have no status register and need to use repair status or other
        // methods to check
        match self {
            PowerDomain::GMAC => None, // GMAC uses repair_status, not regular status register
            _ => {
                let id = *self as u8;
                let reg_offset = match id {
                    0..=31 => offset::PMU_PWR_GATE_STS0,
                    32..=38 => offset::PMU_PWR_GATE_STS1,
                    _ => unreachable!(),
                };

                let bit = if id >= 32 { id - 32 } else { id };
                let mask = 1u32 << bit;

                Some((reg_offset, mask))
            }
        }
    }

    /// Get repair status register info (for special domains like GMAC)
    ///
    /// Returns: Some((offset, mask)) if repair status exists
    ///          None if the domain does not use repair status
    pub fn repair_status_register_info(&self) -> Option<(usize, u32)> {
        match self {
            PowerDomain::GMAC => Some((offset::PMU_BISR_STS4, 1u32 << 21)), // BIT(21)
            _ => None,
        }
    }

    /// Get BIU idle control register info
    pub fn idle_register_info(&self) -> Option<(usize, u32, u32)> {
        // Not all power domains have BIU interface
        // Here lists power domains with BIU interface
        let (reg_offset, bit) = match self {
            PowerDomain::GPU => (offset::PMU_IDLE_SFTCON0, 0),
            PowerDomain::NPUTOP => (offset::PMU_IDLE_SFTCON0, 1),
            PowerDomain::NPU1 => (offset::PMU_IDLE_SFTCON0, 2),
            PowerDomain::NPU2 => (offset::PMU_IDLE_SFTCON0, 3),
            PowerDomain::VENC0 => (offset::PMU_IDLE_SFTCON0, 4),
            PowerDomain::VENC1 => (offset::PMU_IDLE_SFTCON0, 5),
            PowerDomain::RKVDEC0 => (offset::PMU_IDLE_SFTCON0, 6),
            PowerDomain::RKVDEC1 => (offset::PMU_IDLE_SFTCON0, 7),
            PowerDomain::VDPU => (offset::PMU_IDLE_SFTCON0, 8),
            PowerDomain::AV1 => (offset::PMU_IDLE_SFTCON0, 9),
            PowerDomain::ISP1 => (offset::PMU_IDLE_SFTCON0, 10),
            PowerDomain::RGA31 => (offset::PMU_IDLE_SFTCON0, 11),
            PowerDomain::VOP => (offset::PMU_IDLE_SFTCON0, 12),
            PowerDomain::VO0 => (offset::PMU_IDLE_SFTCON0, 13),
            PowerDomain::VO1 => (offset::PMU_IDLE_SFTCON0, 14),
            PowerDomain::AUDIO => (offset::PMU_IDLE_SFTCON0, 15),

            PowerDomain::NVM => (offset::PMU_IDLE_SFTCON1, 0),
            PowerDomain::SDIO => (offset::PMU_IDLE_SFTCON1, 1),
            PowerDomain::USB => (offset::PMU_IDLE_SFTCON1, 2),
            PowerDomain::PHP => (offset::PMU_IDLE_SFTCON1, 3),
            PowerDomain::SECURE => (offset::PMU_IDLE_SFTCON1, 4),
            PowerDomain::CENTER => (offset::PMU_IDLE_SFTCON1, 5),

            _ => return None, // No BIU interface
        };

        let mask = 1u32 << bit;
        let write_mask = mask << 16;
        Some((reg_offset, mask, write_mask))
    }

    /// Get BIU idle status register info
    pub fn idle_status_register_info(&self) -> Option<(usize, u32)> {
        let (idle_con_offset, mask, _) = self.idle_register_info()?;

        // Status register offset corresponds to control register
        let status_offset = match idle_con_offset {
            offset::PMU_IDLE_SFTCON0 => offset::PMU_IDLE_STS0,
            offset::PMU_IDLE_SFTCON1 => offset::PMU_IDLE_STS1,
            offset::PMU_IDLE_SFTCON2 => offset::PMU_IDLE_STS1,
            _ => return None,
        };

        Some((status_offset, mask))
    }

    /// Get BIU idle acknowledgment status register info
    pub fn idle_ack_register_info(&self) -> Option<(usize, u32)> {
        let (idle_con_offset, mask, _) = self.idle_register_info()?;

        let ack_offset = match idle_con_offset {
            offset::PMU_IDLE_SFTCON0 => offset::PMU_IDLE_ACK_STS0,
            offset::PMU_IDLE_SFTCON1 => offset::PMU_IDLE_ACK_STS1,
            offset::PMU_IDLE_SFTCON2 => offset::PMU_IDLE_ACK_STS1,
            _ => return None,
        };

        Some((ack_offset, mask))
    }
}

/// Power state
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PowerState {
    /// Power off
    Off = 0,
    /// Power on
    On  = 1,
}
